package design.structure_proxy;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import design.structure_proxy.cglib.CglibProxyCallBack;
import design.structure_proxy.design.HelloProxy;
import design.structure_proxy.java.JavaProxy;
import org.junit.Test;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.DateFormat;

/**
 * 代理演示
 */
public class DemoProxy {
    //
    @Test
    public void testDesign(){
        Hello hello=new HelloImpl();
        Hello designHello= design(hello);
        designHello.say();
        DateFormat.getDateInstance();
    }

    @Test
    public void testJava(){
        Hello hello=new HelloImpl();
        Hello javaHello=java(hello);
        javaHello.say();
    }

    @Test
    public void testCglib(){
        Hello cgHello=cglib(HelloImpl.class);
        cgHello.say();
    }

    /**
     * 缺点是如果还有另外的接口（Userddd）实现类需要做代理，那还得去写一个UserProxy去实现User接口
     */
    public static HelloProxy design(Hello hello){
        HelloProxy proxy=new HelloProxy(hello);
        return proxy;
    }

    /**
     * 优点是如果有多个不同的接口需要做同一类代理，可以进行复用，基于字节码在内存中自动生成接口的代理类
     * 缺点是只有对实现了接口中的类进行代理
     * 运行时加上 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
     * 会将JDK动态代理生成的字节码保存到硬盘中（项目根目录的com.sun.proxy包下生成java@Proxy0.class）
     * @param o 为需要代理的目标类
     * @param <T>
     * @return
     */
    public static<T> T java(T o){
        JavaProxy javaProxy=new JavaProxy(o);
        /**
         * 实现原理为：在通过调用Proxy.newProxyInstance方法时会通过字节码在内存中生成代理类$Proxy0.class
         * 该代理类中继承自Proxy实现了目标类的所有接口（此处为Hello），在实现的方法中会调用父类Proxy的InvocationHandler(此处为JavaProxy)属性对象的invoke方法
         */
        T proxyObj=(T)Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),javaProxy);
        return proxyObj;
    }

    /**
     * 优点是可以对类进行代理
     */
    public static<T> T cglib(Class<T> cls){
        //将cglib在内存中生成的代理类保存在D:\\code文件夹中，方便反编译查看原理
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setUseCache(false);
        enhancer.setCallback(new CglibProxyCallBack());
        T proxyObj=(T)enhancer.create();
        return proxyObj;
    }

    /**
     *测试方法区溢出
     * -XX:PermSize=1m -XX:MaxPermSize=1m
     */
    public static void testOutOfMemoryError(){
        boolean flag=true;
        while (flag) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(HelloImpl.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(o,objects);
                }
            });
            enhancer.create();
        }
    }
}

